// Author: wsfuyibing <682805@qq.com>
// Date: 2025-05-01

package authorizer

import (
	"gitee.com/go-libs/config"
	"gitee.com/go-libs/log"
	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/middleware/jwt"
	"regexp"
	"sync"
	"time"
)

const (
	authorizeKey   = "__ContextAuthentication__"
	defaultHeader  = "Authorization"
	defaultSeconds = 2592000
	defaultSecret  = "__ContextAuthenticationSecret__"
)

var (
	once                  = new(sync.Once)
	regexMatchToken       = regexp.MustCompile(`(\S+)$`)
	singletonAuthorizer   Authorizer
	unknownAuthentication = &Authentication{}
)

type (
	// Authorizer is a component interface used to build jwt and verify it.
	// It's also used to bind authentication to iris context.
	Authorizer interface {
		// Build json web token (jwt) with given authentication (payload).
		Build(auth *Authentication) (token string, seconds int, err error)

		// Get authentication from iris context that success verified.
		Get(i iris.Context) *Authentication

		// GetHeaderName returns the header name that passed by http request
		// header .
		GetHeaderName() string

		// GetHeaderToken returns the header json web token that passed by
		// http request header.
		GetHeaderToken(i iris.Context) string

		// Set authentication to iris context.
		Set(i iris.Context, authentication *Authentication)

		// Unset authentication from iris context.
		Unset(i iris.Context)

		// Verify json web token to authorization.
		Verify(token string) (authentication *Authentication, err error)
	}

	authorizer struct {
		Duration time.Duration `json:"-" yaml:"-"`
		Header   string        `json:"header" yaml:"header"`
		Seconds  int           `json:"seconds" yaml:"seconds"`
		Secret   string        `json:"secret" yaml:"secret"`

		signer   *jwt.Signer
		verifier *jwt.Verifier
	}
)

// New return the singleton authorizer instance.
func New() Authorizer {
	return singletonAuthorizer
}

func (o *authorizer) Build(auth *Authentication) (token string, seconds int, err error) {
	var body []byte
	if body, err = o.signer.Sign(auth); err == nil {
		token = string(body)
		seconds = o.Seconds
	}
	return
}

func (o *authorizer) Get(i iris.Context) *Authentication {
	if v := i.Values().Get(authorizeKey); v != nil {
		if g, ok := v.(*Authentication); ok {
			return g
		}
	}
	return unknownAuthentication
}

func (o *authorizer) GetHeaderName() string {
	return o.Header
}

func (o *authorizer) GetHeaderToken(i iris.Context) string {
	if str := i.GetHeader(o.Header); str != "" {
		if m := regexMatchToken.FindStringSubmatch(str); len(m) == 2 {
			return m[1]
		}
	}
	return ""
}

func (o *authorizer) Set(i iris.Context, authentication *Authentication) {
	i.Values().Set(authorizeKey, authentication)
}

func (o *authorizer) Unset(i iris.Context) {
	i.Values().Remove(authorizeKey)
}

func (o *authorizer) Verify(token string) (authentication *Authentication, err error) {
	var verified *jwt.VerifiedToken

	// Verify ok.
	if verified, err = o.verifier.VerifyToken([]byte(token)); err != nil {
		return
	}

	// Claim ok.
	authentication = &Authentication{}
	err = verified.Claims(authentication)
	return
}

func (o *authorizer) init() *authorizer {
	// Scan config.
	if err := config.Seek("authorize.yaml", "authorize.yml").ScanYaml(o); err != nil {
		log.Errorf(`scan authorizer config: %v`, err)
	}

	// Fill with default.
	if o.Secret == "" {
		o.Secret = defaultSecret
	}

	// Default lifetime
	if o.Seconds == 0 {
		o.Seconds = defaultSeconds
	}
	o.Duration = time.Second * time.Duration(o.Seconds)

	// Default header name.
	if o.Header == "" {
		o.Header = defaultHeader
	}

	// Build signer and verifier and permissions.
	o.signer = jwt.NewSigner(jwt.HS256, o.Secret, o.Duration)
	o.verifier = jwt.NewVerifier(jwt.HS256, o.Secret)
	return o
}

func (o *authorizer) initLoader() {
}

func init() {
	once.Do(func() {
		singletonAuthorizer = (&authorizer{}).init()
	})
}
